using System;
using System.Collections.Generic;
namespace Framework
{
    public static partial class Utils
    {
        public static class Assembly
        {
            private static readonly System.Reflection.Assembly[] s_Assemblies = null;
            private static readonly Dictionary<string, Type> s_CachedTypes = new Dictionary<string, Type>();
            public static readonly string[] AssemblyNames = { "Assembly-CSharp" };
            public static readonly string[] EditorAssemblyNames = { "Assembly-CSharp-Editor" };

            static Assembly()
            {
                s_Assemblies = AppDomain.CurrentDomain.GetAssemblies();
            }
            public static System.Type[] GetTypeNames(System.Type typeBase)
            {
                return GetTypeNames(typeBase, AssemblyNames);
            }
            public static System.Type[] GetEditorTypes(System.Type typeBase)
            {
                return GetTypeNames(typeBase, EditorAssemblyNames);
            }
            public static System.Reflection.Assembly GetAssembly(string name)
            {
                System.Reflection.Assembly assembly = null;
                try
                {
                    assembly = System.Reflection.Assembly.Load(name);
                }
                catch (System.Exception)
                {

                }
                return assembly;
            }
            public static void ForeachType(System.Type typeBase, System.Reflection.Assembly assembly, Action<Type> filter)
            {
                System.Type[] types = assembly.GetTypes();
                foreach (System.Type type in types)
                {
                    if (typeBase.IsAssignableFrom(type))
                    {
                        filter.Invoke(type);
                    }
                }
            }
            private static System.Type[] GetTypeNames(System.Type typeBase, string[] assemblyNames)
            {
                List<System.Type> typeNames = new List<Type>();
                foreach (string assemblyName in assemblyNames)
                {
                    System.Reflection.Assembly assembly = GetAssembly(assemblyName);
                    if (assembly == null)
                    {
                        continue;
                    }
                    System.Type[] types = assembly.GetTypes();
                    foreach (System.Type type in types)
                    {
                        if (type.IsClass && !type.IsAbstract && typeBase.IsAssignableFrom(type))
                        {
                            typeNames.Add(type);
                        }
                    }
                }
                // typeNames.Sort();
                return typeNames.ToArray();
            }

            public static System.Reflection.Assembly[] GetAssemblies()
            {
                return s_Assemblies;
            }


            public static Type[] GetTypes()
            {
                List<Type> results = new List<Type>();
                foreach (System.Reflection.Assembly assembly in s_Assemblies)
                {
                    results.AddRange(assembly.GetTypes());
                }
                return results.ToArray();
            }


            public static void GetTypes(List<Type> results)
            {
                if (results == null)
                {
                    throw new Exception("Results is invalid.");
                }
                results.Clear();
                foreach (System.Reflection.Assembly assembly in s_Assemblies)
                {
                    results.AddRange(assembly.GetTypes());
                }
            }

            public static Type GetType(string typeName, System.Reflection.Assembly assembly = null)
            {
                if (string.IsNullOrEmpty(typeName))
                {
                    throw new Exception("Type name is invalid.");
                }
                if (s_CachedTypes.TryGetValue(typeName, out Type type))
                {
                    return type;
                }
                type = Type.GetType(typeName);
                if (type != null)
                {
                    s_CachedTypes.Add(typeName, type);
                    return type;
                }
                if (assembly != null)
                {
                    type = Type.GetType(Text.Format("{0}, {1}", typeName, assembly.FullName));
                    if (type != null)
                    {
                        s_CachedTypes.Add(typeName, type);
                        return type;
                    }
                }
                else
                {
                    foreach (System.Reflection.Assembly item in s_Assemblies)
                    {
                        type = Type.GetType(Text.Format("{0}, {1}", typeName, item.FullName));
                        if (type != null)
                        {
                            s_CachedTypes.Add(typeName, type);
                            return type;
                        }
                    }
                }

                return null;
            }
        }
    }
}